package com.heo.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceModelEntity;
import com.heo.pojo.JsonArgs;
import com.heo.pojo.OrgInfoEntity;
import com.heo.pojo.StatusDictEntity;
import com.heo.protocol.packet.SocketPacket;
import com.heo.protocol.packet.StartDeviceResp;
import com.heo.pub.ApplicationContextUtil;
import com.heo.pub.DictEnum;
import com.heo.pub.ServerConfig;
import com.heo.queue.task.ReSendResponseInfo;
import com.heo.service.DeviceInfoService;
import com.heo.service.DeviceModelService;
import com.heo.service.OrgInfoService;
import com.heo.service.StatusDictService;
import com.heo.task.CallBackTaskQueue;
import com.heo.task.SendTaskQueuePriority;
import com.heo.task.thread.CallBackInfo;
import com.heo.task.thread.WaterSendInfo;
import com.heo.util.ByteHelper;
import com.heo.util.DeviceNettyUtils;
import com.heo.util.DeviceServiceUtils;
import com.heo.util.NettyChannelMapUtils;
import com.heo.util.RemoteUpgrade;
import com.heo.util.SequenceHelper;
import com.heo.util.Utils;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * Created by zhangcf handler不是一个单例.即每个channel下都会有自己的一个handler实例.
 * 对于使用spring注解时，一定要注解该类为多例。否则通道断开再重新建立时，会提示 NettyServerHandler is not
 * a @Sharable handler, so can't be added or removed multiple times.
 */
@Component
@Scope("prototype")
public class NettyServerTransparentHandle extends SimpleChannelInboundHandler<String> {

	private static Logger logger = Logger.getLogger(NettyServerTransparentHandle.class);
	private static final int SLEEP_TIME = 1000;// 每次休息100毫秒
	public ChannelHandlerContext mCtx;
	private String deviceImei = null;
	private int modelType = 0;
	private String macAddress = null;
	private String query = ""; // 服务器与板子之间的查询
	private String heartbeat = ""; // 板子跟洗衣机板子之间的心跳
	private String startCmd = ""; // 投币确认信号指令
	private String receiveScriptEngine = "";
	private String sendScriptEngine = "";
	private String responseScriptEngine = "";
	private String appId = "";
	private String callUrl = "";
	private String currentFirmwareVer =  "";
	private String currentHardVer =  "";
	//private String queryHardVersion = "";
	private int timeoutCount = 0;
	private Map<String, StatusDictEntity> statusDictMap;
	private JSONObject paramsMap;
//	private int withoutCode;
//	private int balances = 0;
//	private int score = 0;
	
	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		// TODO Auto-generated method stub
		System.out.println("7B0622A30001B9CA".substring(7, 8));
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		this.mCtx = ctx;
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
		try {
			String jsonString = msg;
			JSONObject rspJsonMap = null;
			logger.info("macAddress = " + macAddress + ", read message = " + jsonString);
			JsonArgs jsonArgs = JSONObject.parseObject(jsonString, JsonArgs.class);// (JsonArgs)
																					// GsonUtils.jsonToObject(jsonString,
																					// JsonArgs.class);
			if (jsonArgs != null) {
				Integer option = jsonArgs.getO();
				if (Utils.checkNull(jsonArgs.getM())) {
					macAddress = jsonArgs.getM();
				}
				
				if (!Utils.checkNull(macAddress)) {
					throw new Exception(macAddress + "设备不存在请联系管理员!!");
				}

				if (Utils.checkNull(option)) {
					// scriptEngine = "function coll(cmd){if(cmd ==
					// 'FFFF0C000000000000030000000413'){return '00';} if(cmd ==
					// 'FFFF08000000000000050D'){return '03';} if(cmd.length < 34){return '-1'}
					// if(cmd.length > 36){var er = cmd.substring(34, 36); if(('21' != er && 'F1' !=
					// er) && ('51' == er || '71' == er || '41' == er || 'E1' == er)) {return er}
					// var ss = cmd.substring(32, 34); if(cmd.length == 34){ss = cmd.substring(24,
					// 26);} var st = '-1'; if('01' == ss || '00' == ss || '0E' == ss) { st = '01';}
					// else if('02' == ss || '03' == ss || '05' == ss || '06' == ss || '07' == ss ||
					// '08' == ss || '09' == ss || '0A' == ss || '0B' == ss){st = '00';} return
					// st;}} coll(cmd)";
					rspJsonMap = new JSONObject();
					rspJsonMap.put("o", option);
					timeoutCount = 0;
					switch (option) {
					case 1:
						// 初始化设备，并入库
						DeviceInfoService deviceInfoService = (DeviceInfoService) ApplicationContextUtil
								.getBeanById("deviceInfoService");
						DeviceModelService deviceModelService = (DeviceModelService) ApplicationContextUtil
								.getBeanById("deviceModelService");
						StatusDictService statusDictService = (StatusDictService) ApplicationContextUtil
								.getBeanById("statusDictService");
						OrgInfoService orgInfoService = (OrgInfoService) ApplicationContextUtil
								.getBeanById("orgInfoService");
						DeviceInfoEntity deviceInfo = null;
						if (macAddress.length() == 15) {
							deviceInfo = deviceInfoService.getDeviceInfoMapByKey(macAddress);
						} else {
							deviceInfo = deviceInfoService.getDeviceInfoMap(macAddress);
						}
						if (deviceInfo == null) {
							throw new Exception(macAddress + "设备不存在请联系管理员!!");
						}
						deviceImei = deviceInfo.getDeviceImei();
						currentHardVer = deviceInfo.getCurrentHardVer();
						DeviceModelEntity deviceModel = deviceModelService
								.getDeviceModelMap(deviceInfo.getDeviceModelId());
						receiveScriptEngine = deviceModel.getReceivePretreatmentCode();
						sendScriptEngine = deviceModel.getSendPretreatmentCode();
						responseScriptEngine = deviceModel.getResponsePretreatmentCode();
						jsonArgs.setM(deviceInfo.getDeviceMac());
						deviceInfoService.initDeviceInfo(jsonArgs);
						OrgInfoEntity orgInfoEntity = orgInfoService.getByKey(deviceInfo.getDeviceImei());
						appId = orgInfoEntity.getAppId();
						callUrl = orgInfoEntity.getCallUrl();
						modelType = deviceModel.getDeviceModelId();
						NettyChannelMapUtils.add(appId, deviceInfo.getDeviceImei(), deviceInfo.getDeviceChannelNum(),
								Utils.getShort(deviceModel.getDeviceModel()), ctx.channel());
						//配置设备的默认参数
						DeviceNettyUtils.deviceInitResult(rspJsonMap);
						statusDictMap = statusDictService.getStatusDictMap(deviceModel.getDeviceModelId());
						int b = Utils.getInt(DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "04"));
						if(b <= 0) {
							b = 9600;
						}
						rspJsonMap.put("b", b);
						//add by:xuyongwen 20190511  发送设备心跳间隔
						rspJsonMap.put("t", deviceInfo.getHeartInterval()); // 心跳
						rspJsonMap.put("n", deviceInfo.getRetryCount()); // 重试次数
						rspJsonMap.put("i", deviceInfo.getAnswerTimeout()); // 应答超时时间
						
						sendMessage(rspJsonMap);
						heartbeat = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "01");
						query = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "02");
						startCmd = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "03");
						String filter = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "05");
						String a  = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "06");
						
						// 登录发送查询指令软件版本号
//						queryHardVersion = ServerConfig.QUERY_HARD_VERSION;
//						logger.info("macAddress = " + macAddress + "设备软件版本号" + currentHardVer + "新协议设备软件版本号" + queryHardVersion);
						
						if(modelType == DictEnum.ModelType.CARD_RECHARGE){
							paramsMap = new JSONObject();
							rspJsonMap.clear();
							rspJsonMap.put("h", "056600002299");
							rspJsonMap.put("o", 4);
							WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 2000);
							SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
						}else if (modelType == DictEnum.ModelType.A02_WASH_MACHINE) {
							paramsMap = new JSONObject();
							rspJsonMap.clear();
							rspJsonMap.put("h", "534A0C010200000000000016");
							rspJsonMap.put("o", 4);
							WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 2000);
							SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
						}
						
						// 发送模块跟主板之间的心跳
						if (Utils.checkNull(heartbeat)) {
							if(!Utils.checkNull(a)) {
								a = "10";
							}
							rspJsonMap.clear();
							rspJsonMap.put("h", heartbeat);
							rspJsonMap.put("a", Integer.parseInt(a));
							rspJsonMap.put("o", 8);
							rspJsonMap.put("f", filter);
//							SendMessageTransparentThread sendMessageTransparentThread = new SendMessageTransparentThread(
//									appId, deviceImei, rspJsonMap.toJSONString(), 5000);
//							NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageTransparentThread);
							
							WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 4000);
							SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
						}
						
						// 发送模块与设备之间的查询状态指令
						if(Utils.checkNull(query)){
							//该设备安装的软件版本是否支持发送查询指令
							boolean isSupportQuery = false;
							if(currentHardVer != null) {
								//两种新协议{"o":10}的版本号8.1.4，D1.1.4
								String hardVer = currentHardVer;
								String str = hardVer.substring(0, 1);
								hardVer = hardVer.replaceAll("[a-zA-Z]","" );
								hardVer = hardVer.replace(".", "");
								if(str.equals("D")) {
									if(Integer.parseInt(hardVer) >= 114) {
										isSupportQuery = true;
									}
								}else if(str.equals("Z") || str.equals("Y")){
									isSupportQuery = true;
								}else {
									if(Integer.parseInt(hardVer) >= 814) {
										isSupportQuery = true;
									}
								}
							}
							if(isSupportQuery) {
								if(!Utils.checkNull(a)) {
									a = "10";
								}
								rspJsonMap.clear();
								rspJsonMap.put("o", 10);
								rspJsonMap.put("h", query);
								rspJsonMap.put("a", Integer.parseInt(a));
								WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 6000);
								SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
							}
						}
						
						
						break;
					case 2:
						if (NettyChannelMapUtils.get(appId, deviceImei) == null) {
//							NettyChannelMapUtils.remove(appId, deviceImei);
							close();
						}
						
//						if(Utils.checkNull(query)){
//							rspJsonMap.put("o", 4);
//							rspJsonMap.put("h", query);
//							WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 4000);
//							SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
//						}
						
						// 发送模块与设备之间的查询状态指令
						if(Utils.checkNull(query)){
							//判断模块是否写入查询指令成功
							if(!NettyChannelMapUtils.deviceQueryStatus(appId, deviceImei, mCtx)) {
								//该设备安装的软件版本是否支持发送查询指令
								boolean isSupportQuery = false;
								if(currentHardVer != null) {
									//两种新协议{"o":10}的版本号8.1.4，D1.1.4
									String hardVer = currentHardVer;
									String str = hardVer.substring(0, 1);
									hardVer = hardVer.replaceAll("[a-zA-Z]","" );
									hardVer = hardVer.replace(".", "");
									if(str.equals("D")) {
										if(Integer.parseInt(hardVer) >= 114) {
											isSupportQuery = true;
										}
									}else if(str.equals("Z") || str.equals("Y")){
										isSupportQuery = true;
									}else {
										if(Integer.parseInt(hardVer) >= 814) {
											isSupportQuery = true;
										}
									}
								}
								
								if(isSupportQuery) {
									logger.info("macAddress = " + macAddress + "设备连接时接收指令失败，在心跳指令中重发设备指令");
									String a1  = DeviceNettyUtils.scriptEngineCmd(sendScriptEngine, "06");
									if(!Utils.checkNull(a1)) {
										a1 = "60";
									}
									rspJsonMap.clear();
									rspJsonMap.put("o", 10);
									rspJsonMap.put("h", query);
									rspJsonMap.put("a", Integer.parseInt(a1));
									WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 4000);
									SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
								}else {
									rspJsonMap.put("o", 4);
									rspJsonMap.put("h", query);
									WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 4000);
									SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
								}
							}
						}
						
						int signal = 0;
						String signalStr = jsonArgs.getH();
						if(Utils.checkNull(signalStr)){
							signal = Utils.getInt(signalStr);
						}
						
						if(signal > 0) {
							NettyChannelMapUtils.updateDeviceSignal(appId, deviceImei, signal, mCtx);
						}else {
							NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
						}
						rspJsonMap.clear();
						rspJsonMap.put("o", 2);
						sendMessage(rspJsonMap);
						break;
					case 3:
						if (!Utils.checkNull(deviceImei)) {
							throw new Exception("设备不存在请联系管理员!!");
						}
						Boolean isSleep = false;//是否需要休眠
						//rspJsonMap.put("t", 150); // 心跳
						
						//add by:xuyongwen
						DeviceInfoService deviceInfoServiceOne = (DeviceInfoService) ApplicationContextUtil
								.getBeanById("deviceInfoService");
						DeviceInfoEntity deviceInfoOne = null;
						if (macAddress.length() == 15) {
							deviceInfoOne = deviceInfoServiceOne.getDeviceInfoMapByKey(macAddress);
						} else {
							deviceInfoOne = deviceInfoServiceOne.getDeviceInfoMap(macAddress);
						}
						rspJsonMap.put("t", deviceInfoOne.getHeartInterval()); // 心跳 
						
						String h = jsonArgs.getH();
						if (Utils.checkNull(h)) {
							// NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);//
							// 设备有消息上来，在redis里做延期
							String cmd = receiveScriptEngine == "" ? h : DeviceNettyUtils.scriptEngineCmd(receiveScriptEngine, h);
							logger.info("硬件设备状态  = " + cmd);
							//add by:xuyongwen  20200920
							//通过数据库脚本对多通道状态进行解析
							if (cmd != null && !cmd.equals("") && cmd.startsWith("AAAA")) {//模块上传通道状态
								String channels = cmd.substring(4);
								logger.info("AAAA充电桩通道状态  = " + channels);
								int count = channels.length()/2;//通道数
								for (int i = 0; i < count; i++) {
									int channelNum = count - i - 1;
									String channelStatus = channels.substring((count - i -1)*2, (count - i)*2);
									//logger.info("充电桩通道  = " + i + ";状态码 = " + channelStatus);
									if(channelStatus.equals("00")) {//工作中
										NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, channelNum,
												DictEnum.CallBackType.STATUS_REPORT, "00", callUrl, statusDictMap, mCtx);
									}else if (channelStatus.equals("01")) {//空闲
										NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, channelNum,
												DictEnum.CallBackType.STATUS_REPORT, "01", callUrl, statusDictMap, mCtx);
									}
								}
							}
							if (cmd != null && !cmd.equals("") && cmd.startsWith("BBBB")) {//模块上传通道状态
								String channels = cmd.substring(4);
								logger.info("BBBB充电桩通道状态  = " + channels);
								int count = channels.length()/4;//通道数
								for (int i = 0; i < count; i++) {
									String channelInfo = channels.substring((count - i -1)*4, (count - i)*4);
									String channelNum = channelInfo.substring(0,2);
									String channelStatus = channelInfo.substring(2,4);
									logger.info("BBBB充电桩通道  = " + channelNum + ";状态码 = " + channelStatus);
									if(channelStatus.equals("00")) {//工作中
										NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, Integer.valueOf(channelNum),
												DictEnum.CallBackType.STATUS_REPORT, "00", callUrl, statusDictMap, mCtx);
									}else if (channelStatus.equals("01")) {//空闲
										NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, Integer.valueOf(channelNum),
												DictEnum.CallBackType.STATUS_REPORT, "01", callUrl, statusDictMap, mCtx);
									}
								}
							}
							
							//add by:xuyongwen  20190701
							if(responseScriptEngine != null && responseScriptEngine != "") {
								//logger.info("收到指令<"+h+">,开始分析》》》》》》》》》》》》》");
								String rsp = DeviceNettyUtils.scriptEngineCmd(responseScriptEngine, h);
								logger.info("硬件设备反馈指令  = " + rsp);
								if(rsp != null && !rsp.equals("")) {
									isSleep = true;
									JSONObject jsonMap = new JSONObject();
									jsonMap.put("o", 4);
									jsonMap.put("h", rsp);
									jsonMap.put("m", deviceImei);
									sendMessage(jsonMap);
								}
							}
							
							
							//杭州币控充电桩
							if(modelType == DictEnum.ModelType.AIYAXING_CHARGING_PILE) {
								DeviceServiceUtils.aiyaxingChargingPile(appId, deviceImei, cmd, callUrl, statusDictMap, mCtx);
							}else if (modelType == DictEnum.ModelType.CARD_RECHARGE) {
								DeviceServiceUtils.cardRecharge(appId, deviceImei, h, paramsMap);
							}else if(modelType == DictEnum.ModelType.TOUSHI_CHARGING_PILE){
								h = h.trim();
								if (h.length() == 50 && h.startsWith("7B0414")) {//模块上传通道状态
									String channels = h.substring(6, h.length() - 4);
									logger.info("充电桩通道状态  = " + channels);
									int count = channels.length()/4;//通道数
									for (int i = 0; i < count; i++) {
										String channelStatus = channels.substring((count - i -1)*4, (count - i)*4);
										//logger.info("充电桩通道  = " + i + ";状态码 = " + channelStatus);
										if(channelStatus.equals("00FF")) {//工作中
											NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, i,
													DictEnum.CallBackType.STATUS_REPORT, "00", callUrl, statusDictMap, mCtx);
										}else if (channelStatus.equals("0000")) {//空闲
											NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, i,
													DictEnum.CallBackType.STATUS_REPORT, "01", callUrl, statusDictMap, mCtx);
										}
									}
								}
								
//								if(!h.startsWith("7B04")) {
//									JSONObject queryMessage = new JSONObject();
//									queryMessage.put("h", "7B041140000A7F7F");
//									queryMessage.put("o", 4);
//									WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, queryMessage.toJSONString(), 1, 4000);
//									SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
//									String channel = h.substring(7, 8);//
//									int channelId = 9 - Integer.parseInt(channel);
//									NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, channelId,
//											DictEnum.CallBackType.STATUS_REPORT, "00", callUrl, statusDictMap, mCtx);
//									NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, channelId,
//											DictEnum.CallBackType.STATUS_REPORT, "01", callUrl, statusDictMap, mCtx);
//								}
							}else {
								// 03表示投币成功，等待启动
								if (cmd.equals("03")) {
									if (Utils.checkNull(heartbeat)) {
										rspJsonMap.put("h", heartbeat);
									}
								}
								// 04表示需要发送确认信号
								else if (cmd.equals("04") && Utils.checkNull(startCmd)) {
									rspJsonMap.put("h", startCmd);
								} else if (Utils.checkNull(cmd)) {
									NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0,
											DictEnum.CallBackType.STATUS_REPORT, cmd, callUrl, statusDictMap, mCtx);
								}
							}
						}
						
						if(isSleep) {//需要发送反馈指令时，休眠主线程一段时间
							Thread.sleep(SLEEP_TIME);
						}
						sendMessage(rspJsonMap);   //修改   by：xuyongwen  20190719
						break;
					case 4:
						String channelId = ctx.channel().id().asShortText();
						// 获得设备应答包
						logger.info("macAddress = " + macAddress + "获得设备启动应答包:IMEI<"+deviceImei+">;channelId<"+channelId+">");
					    // 收入重发集合里，发送线程需要判断是否要进行重发。
					    ReSendResponseInfo.addResponseInfo(deviceImei, channelId);
						break;
					case 5:
						String hStr = jsonArgs.getH();
						String upgradePack = "";
						if (Utils.checkNull(hStr)) {
							String startStr = hStr.substring(0, 8);
							String lengthStr = hStr.substring(8, 16);
							int start = ByteHelper.hex16ToInt(startStr);
							int length = ByteHelper.hex16ToInt(lengthStr);
							if (length > 0) {
								DeviceModelService service = (DeviceModelService) ApplicationContextUtil.getBeanById("deviceModelService");
								HashMap<String, Object> paraMap = new HashMap<String, Object>();
								paraMap.put("deviceImei", deviceImei);
								deviceModel = service.getByMap(paraMap, "getByDeviceIMEI");
								String firmwarePath = deviceModel.getFirmwareUrl();
								if(!Utils.checkNull(currentFirmwareVer)) {
									// 初始化设备，并入库
									DeviceInfoService deviceInfoServices = (DeviceInfoService) ApplicationContextUtil
											.getBeanById("deviceInfoService");
									DeviceInfoEntity deviceInfos = null;
									if (macAddress.length() == 15) {
										deviceInfos = deviceInfoServices.getDeviceInfoMapByKey(macAddress);
									} else {
										deviceInfos = deviceInfoServices.getDeviceInfoMap(macAddress);
									}
									if (deviceInfos == null) {
										throw new Exception(macAddress + "设备不存在请联系管理员!!");
									}
									currentFirmwareVer = deviceInfos.getCurrentFirmwareVer();
								}
								currentFirmwareVer = currentFirmwareVer == null ? "" : currentFirmwareVer; 
//								if(DictEnum.FirmwareVarEntity.TC_2G.equals(currentFirmwareVer)) {
//									firmwarePath = "tc2g/" + firmwarePath;
//								}else if(DictEnum.FirmwareVarEntity.TC_4G.equals(currentFirmwareVer)) {
//									firmwarePath = "tc4g/" + firmwarePath;
//								}
								firmwarePath = currentFirmwareVer + "/" + firmwarePath;
								if (firmwarePath != null) {
									RemoteUpgrade remoteUpgrade = new RemoteUpgrade(firmwarePath);
									byte[] bt = remoteUpgrade.getUpgradeByte(start, length);
									upgradePack = ByteHelper.byteToHexStringNoSpace(bt);
								}
							}
						}
						rspJsonMap.put("h", upgradePack);
						rspJsonMap.put("o", 5);
						sendMessage(rspJsonMap);
						break;
					case 8:
						break;
					case 9:
						break;
					case 7:
						break;
					case 10:
						// 发送模块与设备之间的查询状态指令 xuyongwen  by：20190520  已弃用
						if(Utils.checkNull(query)){
							//判断模块是否写入查询指令成功
							if(!NettyChannelMapUtils.deviceQueryStatus(appId, deviceImei, mCtx)) {
								logger.info("macAddress = " + macAddress + "更改redis设备信息，设备查询指令已经写入成功");
								//更改redis设备信息，设备查询指令已经写入成功
								NettyChannelMapUtils.updateDeviceQuery(appId, deviceImei, mCtx);
							}
						}
						break;
					case 20:
						// 查询用户余额是否满足圈存机充值金额
						String mStr = jsonArgs.getM();
						String cardnum = jsonArgs.getCardnum();
						Long rechargeMoney = jsonArgs.getMoney();
						Map<String, Object> balanceMap = NettyChannelMapUtils.queryBalance(cardnum);
						if (balanceMap == null) {
							throw new Exception("卡号不存在请联系管理员!!");
						}
						int balances = Utils.getInteger(balanceMap.get("balance"));
						int score = Utils.getInteger(balanceMap.get("score"));
						int realMoney = balances + score;//实际充值数额，用户余额加积分
						
						logger.info("macAddress = " + macAddress + "，卡号："+ cardnum +"，用户余额："+ balances +"，用户积分："+ score +"，卡内余额："+ rechargeMoney);
						if(realMoney > 0) {//用户余额满足充值金额
							rspJsonMap.put("Inmoney", String.valueOf(realMoney));
							rspJsonMap.put("Res", "true");
						}else {
							rspJsonMap.put("Inmoney", "0");
							rspJsonMap.put("Res", "false");
						}
						rspJsonMap.put("o", 20);
						rspJsonMap.put("m", mStr);
						rspJsonMap.put("cardnum", cardnum);
						sendMessage(rspJsonMap);
						break;
					case 21:
						// 查询用户余额是否满足圈存机充值金额
						String mStr1 = jsonArgs.getM();
						String cardnum1 = jsonArgs.getCardnum();
						String Res = jsonArgs.getRes();
						Long balance = jsonArgs.getMoney();
						// 初始化设备，并入库
						DeviceInfoService cardDeviceInfoServices = (DeviceInfoService) ApplicationContextUtil
								.getBeanById("deviceInfoService");
						DeviceInfoEntity cardDeviceInfo = null;
						if (macAddress.length() == 15) {
							cardDeviceInfo = cardDeviceInfoServices.getDeviceInfoMapByKey(macAddress);
						} else {
							cardDeviceInfo = cardDeviceInfoServices.getDeviceInfoMap(macAddress);
						}
						if (cardDeviceInfo == null) {
							throw new Exception(macAddress + "设备不存在请联系管理员!!");
						}
						
						if(Res.equals("true") && balance > 0) {
							//圈存机充值成功后，开始创建用户消费订单，并扣除用户相应余额积分
							String results = NettyChannelMapUtils.createCardRechargeOrder(cardnum1, balance, macAddress);
							if(results.equals("SUCCESS")) {
								
							}
						}
						rspJsonMap.put("o", 21);
						rspJsonMap.put("m", mStr1);
						sendMessage(rspJsonMap);
						break;
					case 22:
						// 查询在线卡用户账户余额
						String mStr2 = jsonArgs.getM();
						String cardnum2 = jsonArgs.getCardnum();
						Map<String, Object> balanceMap1 = NettyChannelMapUtils.queryBalance(cardnum2, macAddress);
						if (balanceMap1 == null) {
							throw new Exception("卡号不存在请联系管理员!!");
						}
						int balances1 = Utils.getInteger(balanceMap1.get("balance"));
						int score1 = Utils.getInteger(balanceMap1.get("score"));
						int realMoney1 = balances1 + score1;//实际充值数额，用户余额加积分
						long currentTime = System.currentTimeMillis();
						
						logger.info("macAddress = " + macAddress + "，卡号："+ cardnum2 +"，用户余额："+ balances1 +"，用户积分："+ score1 +"，卡内余额："+ realMoney1 + "，当前时间戳：" + currentTime);
						
						rspJsonMap.put("o", 22);
						rspJsonMap.put("m", mStr2);
						rspJsonMap.put("cardnum", cardnum2);
						rspJsonMap.put("money", realMoney1);
						rspJsonMap.put("time", currentTime);
						sendMessage(rspJsonMap);
						break;
					case 23:
						// 执行在线卡用户扣费申请
						String mStr3 = jsonArgs.getM();
						String cardnum3 = jsonArgs.getCardnum();
						Long rechargeMoney1 = jsonArgs.getMoney();
						Long realMoney2 = jsonArgs.getLastmoney();;//实际充值数额，用户余额加积分
						String transCsn = jsonArgs.getTransCsn();
						
						if(rechargeMoney1 <= 0) {//用户余额满足充值金额
							throw new Exception("卡消费金额有误，请联系管理员!!");
						}
						
//						Map<String, Object> balanceMap2 = NettyChannelMapUtils.queryBalance(cardnum3, macAddress);
//						if (balanceMap2 == null) {
//							throw new Exception("卡号不存在请联系管理员!!");
//						}
//						
//						int balances2 = Utils.getInteger(balanceMap2.get("balance"));
//						int score2 = Utils.getInteger(balanceMap2.get("score"));
//						int realMoney2 = balances2 + score2;//实际充值数额，用户余额加积分
						
						logger.info("指令23 macAddress = " + macAddress + "，卡号："+ cardnum3 +"，扣费金额："+ rechargeMoney1 +"，卡内余额："+ realMoney2);
						
						rspJsonMap.put("res", "false");
						//只要扣费金额大于零，都处理（2021.02.01需求已确认）
						if(rechargeMoney1 > 0) {
							logger.info("指令23 macAddress = "+ macAddress + "创建消费订单");
							rspJsonMap.put("res", "true");
							//圈存机充值成功后，开始创建用户消费订单，并扣除用户相应余额积分
							NettyChannelMapUtils.createOnlineCardRechargeConsumeOrder(cardnum3, transCsn, rechargeMoney1, macAddress, appId, deviceImei, mCtx);
						}
						
						rspJsonMap.put("o", 23);
						rspJsonMap.put("m", mStr3);
						rspJsonMap.put("cardnum", cardnum3);
						rspJsonMap.put("transCsn", transCsn);
						sendMessage(rspJsonMap);
						break;
					}
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			NettyChannelMapUtils.remove(appId, deviceImei);
			ctx.close();
			close();
		}
	}

	/**
	 * 发送消息
	 * 
	 * @param rspJsonMap
	 * @throws Exception
	 */
	public synchronized void sendMessage(JSONObject rspJsonMap) throws Exception {
		if (rspJsonMap != null && rspJsonMap.size() > 0 && mCtx != null) {
			String rspJson = rspJsonMap.toJSONString();
//			mCtx.writeAndFlush(rspJson + "\n");
//			logger.info("macAddress=" + macAddress + ", send message = " + rspJson);
			DeviceNettyUtils.sendMessage(mCtx.channel(), deviceImei, rspJson);
		}
	}

	public void close() throws IOException {
		if (mCtx != null) {
			NettyChannelMapUtils.remove(appId, deviceImei);
			mCtx.close();
		}
	}
	
	public void sendQuery() {
		// 发送模块与设备之间的查询状态指令
		if(Utils.checkNull(query)){
			//该设备安装的软件版本是否支持发送查询指令
			boolean isSupportQuery = false;
//			String[] hardVers = queryHardVersion.split(",");
//			for (String hardVer : hardVers) {
//				if(currentHardVer != null && hardVer.equals(currentHardVer)) {
//					isSupportQuery = true;
//				}
//			}
			if(currentHardVer != null) {
				//两种新协议{"o":10}的版本号8.1.4，D1.1.4
				String hardVer = currentHardVer;
				String str = hardVer.substring(0, 1);
				hardVer = hardVer.replaceAll("[a-zA-Z]","" );
				hardVer = hardVer.replace(".", "");
				if(str.equals("D")) {
					if(Integer.parseInt(hardVer) >= 114) {
						isSupportQuery = true;
					}
				}else if(str.equals("Z") || str.equals("Y")){
					isSupportQuery = true;
				}else {
					if(Integer.parseInt(hardVer) >= 814) {
						isSupportQuery = true;
					}
				}
			}
//			if(isSupportQuery) {
//				if(!Utils.checkNull(a)) {
//					a = "10";
//				}
//				rspJsonMap.clear();
//				rspJsonMap.put("o", 10);
//				rspJsonMap.put("h", query);
//				rspJsonMap.put("a", Integer.parseInt(a));
//				WaterSendInfo codeSendInfo = new WaterSendInfo(appId, deviceImei, rspJsonMap.toJSONString(), 1, 4000);
//				SendTaskQueuePriority.getInstance().addCallBackMsg(codeSendInfo);
//			}
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		super.channelReadComplete(ctx);
		// 刷新挂起的数据到远端
		ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		logger.info(cause.getMessage());
		if (Utils.checkNull(deviceImei)) {
			NettyChannelMapUtils.remove(appId, deviceImei);
			JSONObject sendJson = new JSONObject();
			sendJson.put("imei", deviceImei);
			CallBackInfo callBackInfo = new CallBackInfo(sendJson.toString(), ServerConfig.SUB_HTTPS_URL + "/deviceApi/updateRequestDate.do?imei=" + deviceImei);
			callBackInfo.setMaxReSendCount(1);
			callBackInfo.setExpireTime(100);
			CallBackTaskQueue.getInstance().addCallBackMsg(callBackInfo);
		}
		logger.info(macAddress + " exceptionCaught channel close");
		ctx.close();
	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) { // 2
			IdleStateEvent event = (IdleStateEvent) evt;
			if(Utils.checkNull(macAddress)) {
				if(timeoutCount == 2) {
					NettyChannelMapUtils.remove(appId, deviceImei);
					ctx.close();
				}else {
					if (event.state() == IdleState.READER_IDLE || event.state() == IdleState.WRITER_IDLE) {
						timeoutCount++;
						JSONObject rspJsonMap = new JSONObject();
//						rspJsonMap.put("o", 2);
						rspJsonMap.put("o", 4);
						rspJsonMap.put("h", query);
						String rspJson = rspJsonMap.toJSONString();
						mCtx.writeAndFlush(rspJson + "\n");
						logger.info("macAddress=" + macAddress + ", send message = " + rspJson);
					} else if (event.state() == IdleState.ALL_IDLE) {
						logger.info(ctx.channel().remoteAddress() + "超时类型：" + event.state() + "断开连接");
						NettyChannelMapUtils.remove(appId, deviceImei);
						ctx.close();
					}
				}
			}else {
				ctx.close();
			}
		}else {
			super.userEventTriggered(ctx, evt);
		}
	}
}
